(function () {
    'use strict';
    var describe = BDD.describe, it = BDD.it, expect = BDD.expect;

    // It is a clear limitation of this framework that it cannot
    // assert on itself at the moment.  The most straightforward
    // way to remedy this would be to throw some sort of exception
    // and to specially handle that in BDD.it where it calls its body.
    // As a result of this limitation, doing actual TDD to add
    // functionality is not possible.

    describe('BDD assertion framework', function () {
        it('expect object to be a(n)', function () {
            expect(12).to.be.a('number');
            expect('').to.be.a('string');
            expect(undefined).to.be.an('undefined');
            expect({}).to.be.an('object');
            expect([]).to.be.an('object');
            expect({}).to.be.an(Object);
            expect([]).to.be.an(Object);
            expect([]).to.be.an(Array);
        });

        it('expect object to have property', function () {
            expect([]).to.haveProperty('length');
            expect({color: 'green'}).to.haveProperty('color');
        });

        describe('expect subset', function () {
            it('for numbers', function () {
                expect([]).to.subset([]);
                expect([]).to.subset([1]);
                expect([1]).to.not.subset([]);
                expect([1]).to.subset([1]);
                expect([1]).to.subset([1, 2]);
                expect([1]).to.subset([2, 1]);
                expect([1, 2]).to.not.subset([]);
                expect([1, 2]).to.not.subset([1]);
                expect([1, 2]).to.subset([1, 2]);
                expect([1, 2]).to.subset([2, 1]);
                expect([2, 1]).to.subset([1, 2]);
                expect([2, 1]).to.subset([2, 1]);
            });

            it('for numbers withCmp(function)', function () {
                expect([1]).to.withCmp(function () { return false; }).not.subset([1]);
                expect([2]).to.withCmp(function () { return true; }).subset([1]);
            });

            it('the empty set is always a subset of everything', function () {
                expect([]).to.withCmp(function () { return false; }).subset([]);
                expect([]).to.withCmp(function () { return false; }).subset([1]);
            });

            it('nothing is ever a subset of the empty set', function () {
                expect([1]).to.withCmp(function () { return true; }).not.subset([]);
            });
        });

        describe('expect exceptions', function () {
            it('to throw anything', function () {
                expect(function () { throw new Error('hello world'); }).to.throw();
            });
            it('to throw an exact match', function () {
                expect(function () { throw new Error('hello world'); }).to.throw('hello world');
            });
            it('to throw a regexp match', function () {
                expect(function () { throw new Error('goodbye world'); }).to.throw(/good/);
            });
            it('to throw with object argument', function () {
                expect(function () { if (this) { throw new Error(''); } }).with(true).to.throw();
                expect(function () { if (this) { throw new Error(''); } }).with(false).to.not.throw();
            });
            it('to throw with other argument', function () {
                expect(function (a) { if (a) { throw new Error(''); } }).with(false, true).to.throw();
                expect(function (a) { if (a) { throw new Error(''); } }).with(true, false).to.not.throw();
            });
            it('to not throw', function () {
                expect(function () { return; }).to.not.throw();
                expect(function () { return; }).to.not.throw('hi');
                expect(function () { return; }).to.not.throw(/hi/);
            });
        });

        describe('object value', function () {
            it('to match string representation', function () {
                expect('goodbye world').to.match(/good/);
                expect(123456).to.match(/234/);
            });
            it('to not match', function () {
                expect('goodbye world').to.not.match(/hello/);
            });

            describe('to equal', function () {
                it('strings', function () {
                    expect('hello world').to.equal('hello world');
                });
                it('undefined', function () {
                    expect(undefined).to.equal(undefined);
                });
                it('objects', function () {
                    expect({color: 'green'}).to.equal({color: 'green'});
                    expect({color: {r: 0, g: 1, b: 0}}).to.equal({color: {r: 0, g: 1, b: 0}});
                });
                it('arrays', function () {
                    expect([12, 34]).to.equal([12, 34]);
                });
                it('withCmp(function)', function () {
                    expect('green').withCmp(function (a, b) { return true; }).to.equal('red');
                });
                it('within(tolerance)', function () {
                    expect(15.001).to.within(0.001).equal(15);
                    expect(14.999).to.within(0.001).equal(15);
                    expect(15.0011).to.within(0.001).not.equal(15);
                    expect(14.9989).to.within(0.001).not.equal(15);
                    expect([15.001]).to.within(0.001).equal([15]);
                    expect([14.999]).to.within(0.001).equal([15]);
                    expect([15.0011]).to.within(0.001).not.equal([15]);
                    expect([14.9989]).to.within(0.001).not.equal([15]);
                });
            });
            describe('to not equal', function () {
                it('strings', function () {
                    expect('hello world').to.not.equal('goodbye world');
                });
                it('objects', function () {
                    expect({color: 'green'}).to.not.equal({color: 'red'});
                    expect({color: 'green'}).to.not.equal({color: {r: 0, g: 1, b: 0}});
                });
                it('arrays', function () {
                    expect([12, 34]).to.not.equal([]);
                });
                it('withCmp(function)', function () {
                    expect('green').withCmp(function (a, b) { return false; }).to.not.equal('green');
                });
            });
        });
    });
}());
